#include <map>
#include <iostream>
#include <fstream>
#include <sstream>

#include "configfactory.h"
#include "enhsimoutput.h"

ConfigFactory::ConfigFactory(const std::string& infilename):
		file_read_(true),
		all_config_options_found_(true) {

	char *filename = (char*)infilename.c_str();
	std::ifstream is(filename);
	if (is.fail()) {
		enh::eout << "Could not open config file " << filename;
		std::cin.get();
		file_read_ = false;
	} else {	
		std::string line;

		//read the config
		while(is.good()) {

			std::getline(is, line);

			if (line.length() >= 2) {
				if (!(line[0] == '#' || isspace(line[0]) || line.substr(2) == "//")) {
					std::stringstream ss(line);
					ss >> line;
					ss >> config_[line];
				}
			}
		}
	}
	is.close();
}

bool ConfigFactory::usingAPbase() {
	return config_["ep_base_stat"] == "ap";
}

bool ConfigFactory::allConfigsFound() {
	return all_config_options_found_;
}

bool ConfigFactory::isFileRead() {
	return file_read_;
}

int ConfigFactory::getConfigInt(const std::string& key) {
	std::string value = config_[key];
	if (value.empty()) {
		enh::eout << key << " not found from config.\n";
		all_config_options_found_ = false;
		return 0;
	}

	return stringToInt(value);
}

f32 ConfigFactory::getConfigFloat(const std::string& key) {
	std::string value = config_[key];
	if (value.empty()) {
		enh::eout << key << " not found from config.\n";
		all_config_options_found_ = false;
		return 0;
	}

	return stringToFloat(value);
}

std::string ConfigFactory::getConfigString(const std::string& key) {
	std::string value = config_[key];
	if (value.empty()) {
		enh::eout << key << " not found from config.\n";
		all_config_options_found_ = false;
		return 0;
	}

	return value;
}

int ConfigFactory::getConfigTalent(const std::string& key) {
	std::string value = config_[key];
	if (value.empty()) {
		enh::eout << key << " not found from config.\n";
		all_config_options_found_ = false;
		return 0;
	}

	return stringToInt(value.substr(0,value.length()-2));
}

float ConfigFactory::getConfigBuff(const std::string& key) {
	std::string value = config_[key];
	if (value.empty()) {
		enh::eout << key << " not found from config.\n";
		all_config_options_found_ = false;
		return 0;
	}

	return stringToFloat(value.substr(0,value.find_first_of("/")));
}

EnhSimConfig& ConfigFactory::buildEnhSimConfig(bool setEP, bool setGraph) {
	if (setEP) {
		sc.setEP(getConfigInt("ep_precision"), 
				 usingAPbase(), 
				 getConfigInt("ep_ap"), 
				 getConfigInt("ep_crit_rating"), 
				 getConfigInt("ep_hit_rating"), 
				 getConfigInt("ep_expertise_rating"), 
				 getConfigInt("ep_haste_rating"), 
				 getConfigInt("ep_mastery_rating"), 
				 getConfigInt("ep_spellpower"), 
				 getConfigFloat("ep_dps"), 
				 getConfigInt("ep_mana"), 
				 getConfigInt("ep_spirit"), 
				 getConfigInt("ep_mp5"),
				 getConfigInt("ep_intellect"),
				 getConfigInt("ep_agility"),
				 getConfigInt("ep_strength"));
	}
	if (setGraph) {
		sc.setGraph(getConfigInt("ep_precision"), 
			usingAPbase(), 
			getConfigInt("ep_ap"), 
			getConfigInt("ep_crit_rating"), 
			getConfigInt("ep_hit_rating"), 
			getConfigInt("ep_expertise_rating"), 
			getConfigInt("ep_haste_rating"), 
			getConfigInt("ep_mastery_rating"), 
			getConfigInt("ep_spellpower"), 
			getConfigFloat("ep_dps"), 
			getConfigInt("ep_mana"), 
			getConfigInt("ep_spirit"), 
			getConfigInt("ep_mp5"),
			getConfigInt("ep_intellect"),
			getConfigInt("ep_agility"),
			getConfigInt("ep_strength"));
	}

#ifdef DEBUG
	sc.setThreads(1, setEP, setGraph);
#else
#ifndef NOTHREADS
	sc.setThreads(getConfigInt("threads"), setEP, setGraph);
#else
	sc.setThreads(1, setEP, setGraph);
#endif
#endif

	sc.setLag(getConfigInt("min_lag"), getConfigInt("max_lag"));
	sc.setSimulateMana(getConfigInt("simulate_mana") == 1);

	//Normal priority list
	int rotation_count = getConfigInt("rotation_priority_count");
	sc.setRotationCount(rotation_count);

	for (int i=0; i<rotation_count; ++i) {
		std::string str = "rotation_priority";
		str.append(intToString(i+1));
		sc.setRotation(config_[str], i, false);
	}

	//AoE priority list
	rotation_count = getConfigInt("aoe_rotation_priority_count");
	sc.setAoeRotationCount(rotation_count);
	
	for (int i=0; i<rotation_count; ++i) {
		std::string str = "aoe_rotation_priority";
		str.append(intToString(i+1));
		sc.setRotation(config_[str], i, true);
	}

	sc.setMhWeaponEnchant(config_["mh_imbue"]);
	sc.setOhWeaponEnchant(config_["oh_imbue"]);

	sc.setRotationSettings(getConfigInt("mh_auto_attack") == 1,
		getConfigInt("oh_auto_attack") == 1,
		getConfigInt("cast_lvb_only_if_fs_active") == 1,
		getConfigInt("cast_fs_only_if_dots_left"),
		getConfigFloat("cast_sr_only_if_mana_left"),
		getConfigInt("cast_sr_on_cooldown") == 1);

	sc.setMhEnchant(config_["mh_enchant"]);
	sc.setOhEnchant(config_["oh_enchant"]);

	sc.setMhWeapon(config_["mh_weapon"]);
	sc.setOhWeapon(config_["oh_weapon"]);

	sc.setRace(config_["race"]);

	sc.setTrinket(config_["trinket1"], 1);
	sc.setTrinket(config_["trinket2"], 2);
	//MW here, because of the worldbreaker_battlegear_4
	sc.setSetBonus(config_["set_bonus1"]);
	sc.setSetBonus(config_["set_bonus2"]);
	sc.setSetBonus(config_["set_bonus3"]);
	sc.setMetagem(config_["metagem"]);
	sc.setMiscEnchant("gloves_enchant", config_["gloves_enchant"]);
	sc.setMiscEnchant("cloak_enchant", config_["cloak_enchant"]);
	sc.setMiscEnchant("ring_proc", config_["ring_proc"]);
	sc.setMiscEnchant("weapon_set_proc", config_["weapon_set_proc"]);

	sc.setGlyph(config_["glyph_major1"]);
	sc.setGlyph(config_["glyph_major2"]);
	sc.setGlyph(config_["glyph_major3"]);

	sc.setGlyph(config_["glyph_minor1"]);
	sc.setGlyph(config_["glyph_minor2"]);
	sc.setGlyph(config_["glyph_minor3"]);

	sc.setArmorTypeBonus(getConfigInt("armor_type_bonus"));

	sc.setMhDps(getConfigFloat("mh_dps"));
	sc.setOhDps(getConfigFloat("oh_dps"));
	sc.setMhSpeed(getConfigFloat("mh_speed") * 100);
	sc.setOhSpeed(getConfigFloat("oh_speed") * 100);
	sc.setMhCrit(getConfigFloat("mh_crit"));
	sc.setOhCrit(getConfigFloat("oh_crit"));
	sc.setMhHit(getConfigFloat("mh_hit"));
	sc.setOhHit(getConfigFloat("oh_hit"));
	sc.setMhExpertiseRating(getConfigFloat("mh_expertise_rating"));
	sc.setOhExpertiseRating(getConfigFloat("oh_expertise_rating"));
	sc.setAttackPower(getConfigInt("ap"));
	sc.setMeleeHaste(getConfigFloat("melee_haste"));
	sc.setSpellHaste(getConfigFloat("spell_haste"));
	sc.setStength(getConfigInt("str"));
	sc.setAgility(getConfigInt("agi"));
	sc.setIntellect(getConfigInt("int"));
	sc.setSpirit(getConfigInt("spi"));
	sc.setMasteryRating(getConfigInt("mastery_rating"));
	sc.setMiss(getConfigFloat("miss"));
	sc.setDodge(getConfigFloat("dodge"));
	sc.setGlancing(getConfigFloat("glancing"));
	sc.setSpellMiss(getConfigFloat("spell_miss"));
	int value = getConfigInt("armor");
	if (value == 10645) { enh::eout << "WotLK Boss Armor should be 10643 not 10645. Are you using the latest config file?" << enh::endl(); }
	sc.setArmor(value);
	sc.setArmorDebuff(getConfigInt("armor_debuff"));
	sc.setNatureResistance(getConfigInt("nature_resistance"));
	sc.setFireResistance(getConfigInt("fire_resistance"));
	sc.setFrostResistance(getConfigInt("frost_resistance"));
	sc.setArcanaResistance(getConfigInt("arcane_resistance"));
	sc.setShadowResistance(getConfigInt("shadow_resistance"));
	sc.setAdditionalTargets(getConfigInt("additional_targets"), getConfigFloat("additional_target_percent"), getConfigInt("additional_target_level"));
	sc.setSpellpower(getConfigInt("spellpower"));
	sc.setSpellCrit(getConfigFloat("spell_crit"));
	sc.setSpellHit(getConfigFloat("spell_hit"));
	sc.setMaxMana(getConfigFloat("max_mana"));
	sc.setMp5(getConfigInt("mp5"));


	sc.setBuff("attack_power_buff_multiplier", getConfigBuff("attack_power_buff_multiplier"));
	sc.setBuff("crit_chance_buff", getConfigBuff("crit_chance_buff"));

	sc.setSpecialization(config_["specialization"]);

	sc.setTalent(config_["talent_tier1"]);
	sc.setTalent(config_["talent_tier2"]);
	sc.setTalent(config_["talent_tier3"]);
	sc.setTalent(config_["talent_tier4"]);
	sc.setTalent(config_["talent_tier5"]);
	sc.setTalent(config_["talent_tier6"]);

	sc.fixStats();

	sc.setBuff("armor_debuff", getConfigBuff("armor_debuff"));
	sc.setBuff("physical_vulnerability_debuff", getConfigBuff("physical_vulnerability_debuff"));
	sc.setBuff("melee_haste_buff", getConfigBuff("melee_haste_buff"));
	sc.setBuff("spell_haste_buff", getConfigBuff("spell_haste_buff"));
	sc.setBuff("spell_crit_chance_debuff", getConfigBuff("spell_crit_chance_debuff"));
	sc.setBuff("spell_damage_debuff", getConfigBuff("spell_damage_debuff"));
	sc.setBuff("spellpower_buff", getConfigBuff("spellpower_buff"));
	sc.setBuff("percentage_damage_increase", getConfigBuff("percentage_damage_increase"));
	sc.setBuff("stat_multiplier", getConfigBuff("stat_multiplier"));
	sc.setBuff("agi_and_strength_buff", getConfigBuff("agi_and_strength_buff"));
	sc.setBuff("mana_buff", getConfigBuff("mana_buff"));
	sc.setBuff("mana_regen_buff", getConfigBuff("mana_regen_buff"));

	sc.setBuff("replenishment", getConfigInt("replenishment"));
	sc.setBuff("water_shield", getConfigInt("water_shield"));
	sc.setBuff("mixology", getConfigInt("mixology"));

	sc.setConsumable(config_["flask_elixir"]);
	sc.setConsumable(config_["guardian_elixir"]);
	sc.setConsumable(config_["potion"]);
	sc.setConsumable(config_["food"]);
	sc.setProfessionBuff(config_["profession_buff"]);

	sc.setUseManaPotionMaxMana(getConfigFloat("use_mana_potion_if_mana_left"));
	sc.setMagmaTotemRefresh(getConfigInt("cast_magma_only_if_ticks_left"));
	sc.setSearingTotemRefresh(getConfigInt("cast_searing_only_if_ticks_left"));
	sc.setPrePottingTime(getConfigInt("use_potion_before_combat"));

	sc.setCombatLength(getConfigFloat("combat_length"), getConfigFloat("combat_length_rng_factor"));
	//sc.useSearingTotem(getConfigInt("use_searing_totem"));
	sc.setBloodlust(getConfigInt("use_bloodlust"));
	sc.setMisc("always_bloodlust_wolves", getConfigInt("always_bloodlust_wolves"));
	

	return sc;
}
